#pragma once
#include <sys/select.h>
#include <string>
#include <iostream>
#include "Sock.hpp"
#include "Log.hpp"

#define READ_EVENT (0x1)
#define WRITE_EVENT (0x1 << 1)
#define EXCEPT_EVENT (0x1 << 2)

typedef struct selectFd_t
{
    int fd;
    uint8_t event; // 读事件、写事件、异常事件
    std::string clientip;
    uint16_t clientport;
} selectFd_t;

const static uint16_t DEFAULTPORT = 8888;
const static int N = sizeof(fd_set) * sizeof(__fd_mask);
const static int DEFAULTFD = -1;

class SelectServer
{
public:
    SelectServer(uint16_t port = DEFAULTPORT) : _port(port)
    {
        for (int i = 0; i < N; ++i)
        {
            _fdarray[i].fd = DEFAULTFD;
            _fdarray[i].event = READ_EVENT;
            _fdarray[i].clientport = 0;
        }
    }
    ~SelectServer()
    {
        _listen_sock.Close();
    }
    void InitServer()
    {
        _listen_sock.Socket();
        _listen_sock.Bind(_port);
        _listen_sock.Listen();
    }

    /*
    IO套接字读就绪时的处理函数
    */
    void Recver(int index)
    {
        int fd = _fdarray[index].fd;
        char buffer[1024];
        ssize_t s = recv(fd, buffer, sizeof(buffer) - 1, 0); // 此时读取会被阻塞吗？不会
        if (s > 0)
        {
            buffer[s - 1] = 0;
            std::cout << _fdarray[index].clientip << ":" << _fdarray[index].clientport << "# " << buffer << std::endl;

            // 写回去，也可以给select管理的，这里就不做了，todo
            std::string echo = "[select server echo]: ";
            echo += buffer;
            send(fd, echo.c_str(), echo.size(), 0);
        }
        else
        {
            if (s == 0)
                logMessage(Info, "client quit ..., fdarray_[i] -> defaultfd: %d->%d", fd, defaultfd);
            else
                logMessage(Warning, "recv error, client quit ..., fdarray_[i] -> defaultfd: %d->%d", fd, defaultfd);
            close(_fdarray[index].fd);
            _fdarray[index].fd = defaultfd;
            _fdarray[index].event = READ_EVENT;
            _fdarray[index].clientip.resize(0);
            _fdarray[index].clientport = 0;
        }
    }

    /*
    监听套接字读事件就绪时的处理函数
    */
    void Acceptor()
    {
        std::string clientip;
        uint16_t clientport;
        // 得到IO套接字
        int sock = _listen_sock.Accept(&clientip, &clientport);
        if (sock < 0) return;
        logMessage(Debug, "[%s:%d], sock: %d", clientip.c_str(), clientport, sock);

        // 将IO套接字及其信息添加到fdarray中，让select管理起来
        int pos = 1;
        for (; pos < N; pos++)
            if (_fdarray[pos].fd == defaultfd)
                break;
        if (pos >= N)
        {
            close(sock);
            logMessage(Warning, "sockfd array[] full");
        }
        else
        {
            _fdarray[pos].fd = sock;
            _fdarray[pos].event = READ_EVENT; // 让select关心IO套接字的读事件
            // _fdarray[pos].event = (READ_EVENT | WRITE_EVENT); // 让select关心IO套接字的读和写事件
            _fdarray[pos].clientip = clientip;
            _fdarray[pos].clientport = clientport;
        }
    }

    void HandleEvent(fd_set &rfds, fd_set &wfds)
    {
        // 扫描fdarray，处理已经就绪的IO事件
        for (int i = 0; i < N; ++i)
        {
            if (_fdarray[i].fd == DEFAULTFD) continue;

            // 读事件就绪
            if ((_fdarray[i].event & READ_EVENT) && FD_ISSET(_fdarray[i].fd, &rfds))
            {
                if (_fdarray[i].fd == _listen_sock.Fd()) // 监听套接字读事件就绪
                    Acceptor();
                else // IO套接字读事件就绪
                    Recver(i);
            }
            // 写事件就绪
            else if ((_fdarray[i].event & WRITE_EVENT) && FD_ISSET(_fdarray[i].fd, &wfds))
            {
                // ...

                // test:
                printf("%d\n", _fdarray[i].fd);
            }
            // 异常就绪
            else
            {
                // ...
            }
        }
    }
    void Start()
    {
        _fdarray[0].fd = _listen_sock.Fd();
        _fdarray[0].event = READ_EVENT;
        while (true)
        {
            fd_set rfds; // select等待的读事件的文件描述符
            fd_set wfds; // select等待的写事件的文件描述符
            FD_ZERO(&rfds);
            FD_ZERO(&wfds);

            int maxfd = _fdarray[0].fd;

            // 扫描fdarray, 重置rfds、wfds、maxfd
            for (int i = 0; i < N; ++i)
            {
                if (_fdarray[i].fd == DEFAULTFD)
                    continue;
                if (_fdarray[i].event & READ_EVENT)
                    FD_SET(_fdarray[i].fd, &rfds);
                if (_fdarray[i].event & WRITE_EVENT)
                    FD_SET(_fdarray[i].fd, &wfds);

                maxfd = _fdarray[i].fd > maxfd ? _fdarray[i].fd : maxfd;
            }

            int n = select(maxfd + 1, &rfds, &wfds, nullptr, nullptr); // 阻塞式等待
            switch (n)
            {
            case 0:
                logMessage(Debug, "Time out, %d: %s", errno, strerror(errno));
                break;
            case -1:
                logMessage(Fatal, "%d: %s", errno, strerror(errno));
                return;
            default:
                logMessage(Debug, "有 %d 个IO事件就绪了。", n);
                HandleEvent(rfds, wfds);
                DebugPrint();
                break;
            }
            sleep(1);
        }
    }
    void DebugPrint()
    {
        std::cout << "_fdarray[]: ";
        for (int i = 0; i < N; ++i)
            if (_fdarray[i].fd != DEFAULTFD)
                std::cout << _fdarray[i].fd << " ";
        std::cout << "\n";
    }

private:
    uint16_t _port;
    Sock _listen_sock;
    selectFd_t _fdarray[N]; // 保存所有需要select等待的fd
};